﻿using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.IsolatedStorage;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Collections.ObjectModel;
using System.Windows.Threading;
using System.Xml.Serialization;


namespace WP7Guide
{
    using System.Collections;
    using System.Reflection;

    public class MainViewModel : ViewModelBase
    {
        public MainViewModel()
        {
            this.Bouquets = new ObservableCollection<BouquetViewModel>();
            Timer=new ObservableCollection<TimerViewModel>();
        }

        public void LoadTimer()
        {
            WebClient webClient = new WebClient();
            webClient.OpenReadCompleted += new OpenReadCompletedEventHandler(webClient_OpenReadCompletedTimer);
            webClient.OpenReadAsync(new Uri( "http://" + App.Settings.DreamboxHost + "/web/timerlist?uid=" + DateTime.Now.Ticks));
        }

        void webClient_OpenReadCompletedTimer(object sender, OpenReadCompletedEventArgs e)
        {
            XmlSerializer ser = new XmlSerializer(typeof(e2timerlist));
            var e2timerlist = (e2timerlist)ser.Deserialize(e.Result);
            Timer.Clear();
            foreach (var e2Timer in e2timerlist.Items)
            {
                var start = ConvertFromUnixTimestamp(e2Timer.e2timebegin);
                var end = ConvertFromUnixTimestamp(e2Timer.e2timeend);
                var item = new TimerViewModel()
                               {
                                   Description = e2Timer.e2description,
                                   DescriptionExtended = e2Timer.e2descriptionextended,
                                   ServiceName = e2Timer.e2servicename,
                                   Title = e2Timer.e2name,
                                   Time = start.ToString() + " - " + end.TimeOfDay.ToString()
                               };
                Timer.Add(item);
            }
        }

        private ObservableCollection<BouquetViewModel> _bouquets;
        public ObservableCollection<BouquetViewModel> Bouquets
        {
            get { return _bouquets; }
            set { _bouquets = value; NotifyPropertyChanged("Bouquets"); }
        }

        [XmlIgnore]
        public ObservableCollection<TimerViewModel> Timer { get; set; }

        private Visibility _progressVisible = Visibility.Collapsed;
        [XmlIgnore]
        public Visibility ProgressVisible
        {
            get { return _progressVisible; }
            private set { _progressVisible = value; NotifyPropertyChanged("ProgressVisible"); }
        }

        internal void HideProgress()
        {
            ProgressVisible = Visibility.Collapsed;
        }

        public DateTime LastUpdate { get; set; }

        private BackgroundWorker _loadWorker;
        private Thread _loadThread;

        /// <summary>
        /// Creates and adds a few ItemViewModel objects into the Items collection.
        /// </summary>
        public void LoadData()
        {
            LoadTimer();

            if ((DateTime.Now - LastUpdate) < App.Settings.UpdateInterval)
            {
                return;
            }

            ProgressVisible = Visibility.Visible;
            
            if (IsDataLoaded)
            {
                _bouquetEnumerator = Bouquets.GetEnumerator();
                ProcessBouquet();
            }
            else
            {
                
                WebClient webClient = new WebClient();
                webClient.OpenReadCompleted += new OpenReadCompletedEventHandler(webClient_OpenReadCompletedBouquets);
                //Dispatcher disp= Dispatcher.CurrentDispatcher;
                
#if FAKE

                Thread _loadThread = new Thread(_loadThread_DoWork);
                _loadThread.Start();
                //_loadWorker= new BackgroundWorker();
                //_loadWorker.DoWork += new DoWorkEventHandler(_loadWorker_DoWork);

                //_loadWorker.RunWorkerAsync();
#else
                webClient.OpenReadAsync(new Uri( "http://" + App.Settings.DreamboxHost + "/web/getservices"));
#endif
            }
        }

        public void CancelLoad()
        {
            _cancelRequested = true;
            if (_loadThread != null)
            {
                if (_loadThread.IsAlive)
                {
                    _loadThread.Abort();
                    _loadThread.Join(500);
                }
            }
        }

        private bool _cancelRequested;

        public void SaveData()
        {
            if (IsolatedStorageSettings.ApplicationSettings.Contains("epg"))
            {
                IsolatedStorageSettings.ApplicationSettings.Remove("epg");
            }
            IsolatedStorageSettings.ApplicationSettings.Add("epg", this);
            IsolatedStorageSettings.ApplicationSettings.Save();
        }

        void _loadWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            webClient_OpenReadCompletedBouquets(this, null);
        }
        void _loadThread_DoWork()
        {
            webClient_OpenReadCompletedBouquets(this, null);
        }


        private IEnumerator<BouquetViewModel> _bouquetEnumerator;
        private IEnumerator<ServiceViewModel> serviceEnumerator;

        void webClient_OpenReadCompletedBouquets(object sender, OpenReadCompletedEventArgs e)
        {
            if (_cancelRequested)
            {
                return;
            }
#if FAKE
            var bouquetStream =
                Application.GetResourceStream(new Uri("SampleData/getbouquets.xml", UriKind.Relative)).Stream;
            //var bouquetStream = e.Result;
#else
            var bouquetStream = e.Result;
#endif

            XmlSerializer ser = new XmlSerializer(typeof(e2servicelist));
            var e2 = (e2servicelist)ser.Deserialize(bouquetStream);

            var bouquets = new ObservableCollection<BouquetViewModel>();
            IEnumerable<e2servicelistrecursiveE2bouquet> items = e2.Items;
            if (e2.Items.Length > App.Settings.MaxBouquets)
            {
                items = e2.Items.Take(App.Settings.MaxBouquets);
            }
            foreach (var bouquet in items)
            {
                if (_cancelRequested)
                {
                    return;
                }

                var item = new BouquetViewModel()
                {
                    Name = bouquet.e2servicename,
                    Details = "",
                    RefID = bouquet.e2servicereference
                };

                bouquets.Add(item);               
                
            }

            IsDataLoaded = true;

            InvokeUI(() => { Bouquets = bouquets; });
            
            _bouquetEnumerator = bouquets.GetEnumerator();
            //enumerator = e2.Items.GetEnumerator();
            ProcessBouquet();
        }
        void webClient_OpenReadCompletedServices(object sender, OpenReadCompletedEventArgs e)
        {
            if (_cancelRequested)
            {
                return;
            }

#if FAKE
            var bouquetStream =
                Application.GetResourceStream(new Uri("SampleData/getservices.xml", UriKind.Relative)).Stream;
            var parent = (BouquetViewModel)sender;
            //var bouquetStream = e.Result;
#else
            var bouquetStream = e.Result;
            var parent = (BouquetViewModel)e.UserState;
#endif

            XmlSerializer ser = new XmlSerializer(typeof(e2servicelist));
            var e2 = (e2servicelist)ser.Deserialize(bouquetStream);
            var services = new ObservableCollection<ServiceViewModel>();
            int i = 0;
            foreach (var service in e2.Items)
            {
                if (_cancelRequested)
                {
                    return;
                }


                if (service.e2servicereference.Contains(service.e2servicename))
                {
                    continue;
                }

                var item = new ServiceViewModel()
                {
                    Name = service.e2servicename,
                    CurrentEvent = "",
                    TimeLeft = "",
                    RefID = service.e2servicereference
                };

                switch (i)
                {
                    case 0:
                        i++;
                        parent.Details = service.e2servicename;
                        break;                   
                    case 1:
                    case 2:
                        i++;
                        parent.Details += ", " + service.e2servicename;
                        break;
                    case 3:
                        i++;
                        parent.Details += ", ...";
                        break;
                }

                services.Add(item);
            }

            InvokeUI(()=>parent.Services=services);
            parent.IsDataLoaded = true;
            
            serviceEnumerator = services.GetEnumerator();
            ProcessService(parent);
        }

        private void ProcessBouquet()
        {
            if (_cancelRequested)
            {
                return;
            }

            if (_bouquetEnumerator.MoveNext())
            {
                var e2Bouquet = _bouquetEnumerator.Current;
                if (e2Bouquet.IsDataLoaded)
                {
                    serviceEnumerator = e2Bouquet.Services.GetEnumerator();
                    ProcessService(e2Bouquet);
                }
                else
                {
                    WebClient webClient = new WebClient();
                    webClient.OpenReadCompleted += new OpenReadCompletedEventHandler(webClient_OpenReadCompletedServices);
#if FAKE
                    webClient_OpenReadCompletedServices(e2Bouquet, null);
#else
                webClient.OpenReadAsync(
                    new Uri("http://" + App.Settings.DreamboxHost + "/web/getservices?sRef=" + e2Bouquet.RefID), e2Bouquet);
#endif                    
                }
            }
            else
            {
                FinalizeData();
            }
            
        }

        private void FinalizeData()
        {
            ResetLoadState();
            LastUpdate = DateTime.Now;
            SaveData();
            ProgressVisible = Visibility.Collapsed;
        }

        public void ResetLoadState()
        {
            foreach (var bouquet in Bouquets)
            {
                foreach (var service in bouquet.Services)
                {
                    service.IsDataLoaded = false;
                }
                bouquet.IsDataLoaded = false;
            }
            IsDataLoaded = false;
            _cancelRequested = false;
        }

        BouquetViewModel CurrentBouquet { get; set; }

        private void ProcessService(BouquetViewModel parent)
        {
            if (_cancelRequested)
            {
                return;
            }

            CurrentBouquet = parent;
            
            if (serviceEnumerator.MoveNext())
            {
                var service = serviceEnumerator.Current;

                if (service.IsDataLoaded)
                {
                    ProcessService(CurrentBouquet);
                }
                else
                {
                    WebClient webClient = new WebClient();
                    webClient.OpenReadCompleted += new OpenReadCompletedEventHandler(webClient_OpenReadCompletedEvents);

#if FAKE
                    webClient_OpenReadCompleted2(service, null);
#else
                    webClient.OpenReadAsync(
                        new Uri("http://" + App.Settings.DreamboxHost + "/web/epgservice?sRef=" + service.RefID + "&endTime=" + TimeSpan.FromDays(App.Settings.DaysToLoad).TotalMinutes), service);
#endif
                }

            }
            else
            {
                ProcessBouquet();
            }
            
        }

        static DateTime ConvertFromUnixTimestamp(string timestamp)
        {
            double ts;
            var n = DateTime.Now;
            //var nu = DateTime.UtcNow;
            var nu = n.ToUniversalTime();
            var diff = n - nu;
            
            if (double.TryParse(timestamp, out ts))
            {

                DateTime origin = new DateTime(1970, 1, 1, 0, 0, 0, 0);
                origin = origin + diff;
                return origin.AddSeconds(ts);
            }
            return DateTime.MinValue;
        }


        void webClient_OpenReadCompletedEvents(object sender, OpenReadCompletedEventArgs e)
        {
            if (_cancelRequested)
            {
                return;
            }

#if FAKE
            var epgStream =
                Application.GetResourceStream(new Uri("SampleData/epgservice.xml", UriKind.Relative)).Stream;
            var parent = (ServiceViewModel)sender;
            //var bouquetStream = e.Result;
#else
            var epgStream = e.Result;
            var parent = (ServiceViewModel)e.UserState;
#endif
#if DEBUG
            Debug.WriteLine(parent.Name);
#endif
            XmlSerializer epgSer = new XmlSerializer(typeof(e2eventlist));
            var events = new ObservableCollection<EventViewModel>();
            var epgData = (e2eventlist)epgSer.Deserialize(epgStream);
            var i = 0;
            if (epgData.Items != null)
            {
                foreach (var e2item in epgData.Items)
                {
                    if (_cancelRequested)
                    {
                        return;
                    }

                    if (e2item.e2eventservicereference.Contains(e2item.e2eventservicename))
                    {
                        continue;
                    }
                    // Add the number of seconds in UNIX timestamp to be converted.

                    var start = ConvertFromUnixTimestamp(e2item.e2eventstart);
                    var duration = GetInteger(e2item.e2eventduration);
                    var end = start.AddSeconds(duration);

                    var item = new EventViewModel()
                                   {
                                       Title = e2item.e2eventtitle,
                                       Time =
                                           start.Hour.ToString() + ":" + start.Minute.ToString("D2") + " - " +
                                           end.Hour.ToString() + ":" + end.Minute.ToString("D2"),
                                       Description = e2item.e2eventdescription,
                                       DescriptionExtended =
                                           e2item.e2eventdescriptionextended.Replace((char) 138, ' '),
                                           EventId=e2item.e2eventid,
                                           EndTime = end
                                   };

                    if (i == 0)
                    {
                        parent.CurrentEvent = e2item.e2eventtitle;
                        var current = ConvertFromUnixTimestamp(e2item.e2eventcurrenttime);
                        var left = start.AddSeconds(duration) - current;
                        parent.TimeLeft = left.Hours.ToString() + ":" + left.Minutes.ToString("D2");
                        i = 1;
                    }

                    events.Add(item);
                }
                InvokeUI(()=>parent.Events=events);
            }
            parent.IsDataLoaded = true;
            ProcessService(CurrentBouquet);
        }


        private int GetInteger(string value)
        {
            int retVal;
            int.TryParse(value, out retVal);
            return retVal;
        }
    }
}